<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">

<head>
    <!-- Book generated using mdBook -->
    <meta charset="UTF-8">
    <title>React - exercisms.io 快速练习</title>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
    <meta name="description" content="exercisms 的练习，网页版.">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="theme-color" content="#ffffff" />

    <link rel="shortcut icon" href="../favicon.png">
    <link rel="stylesheet" href="../css/variables.css">
    <link rel="stylesheet" href="../css/general.css">
    <link rel="stylesheet" href="../css/chrome.css">
    <link rel="stylesheet" href="../css/print.css" media="print">

    <!-- Fonts -->
    <link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
    <link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800"
        rel="stylesheet" type="text/css">
    <link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">

    <!-- Highlight.js Stylesheets -->
    <link rel="stylesheet" href="../highlight.css">
    <link rel="stylesheet" href="../tomorrow-night.css">
    <link rel="stylesheet" href="../ayu-highlight.css">

    <!-- Custom theme stylesheets -->
    
    <link rel="stylesheet" href="../theme/custom.css">
    

    
</head>

<body class="light">
    <!-- Provide site root to javascript -->
    <script type="text/javascript">var path_to_root = "../";</script>

    <!-- Work around some values being stored in localStorage wrapped in quotes -->
    <script type="text/javascript">
        try {
            var theme = localStorage.getItem('mdbook-theme');
            var sidebar = localStorage.getItem('mdbook-sidebar');

            if (theme.startsWith('"') && theme.endsWith('"')) {
                localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
            }

            if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
            }
        } catch (e) { }
    </script>

    <!-- Set the theme before any content is loaded, prevents flash -->
    <script type="text/javascript">
        var theme;
        try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { }
        if (theme === null || theme === undefined) { theme = 'light'; }
        document.body.className = theme;
        document.querySelector('html').className = theme + ' js';
    </script>

    <!-- Hide / unhide sidebar before it is displayed -->
    <script type="text/javascript">
        var html = document.querySelector('html');
        var sidebar = 'hidden';
        if (document.body.clientWidth >= 1080) {
            try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch (e) { }
            sidebar = sidebar || 'visible';
        }
        html.classList.remove('sidebar-visible');
        html.classList.add("sidebar-" + sidebar);
    </script>

    <nav id="sidebar" class="sidebar" aria-label="Table of contents">
        <ol class="chapter"><li><a href="../index.html"><strong aria-hidden="true">1.</strong> 超过 88 道练习，任点</a></li><li><a href="../low.html"><strong aria-hidden="true">2.</strong> 易</a></li><li><ol class="section"><li><a href="../hello-world/README.zh.html"><strong aria-hidden="true">2.1.</strong> hello world</a></li><li><a href="../gigasecond/README.zh.html"><strong aria-hidden="true">2.2.</strong> 千兆秒 &gt;&lt; Gigasecond)</a></li><li><a href="../leap/README.zh.html"><strong aria-hidden="true">2.3.</strong> 闰年 &gt;&lt; Leap</a></li><li><a href="../raindrops/README.zh.html"><strong aria-hidden="true">2.4.</strong> 雨滴声 &gt;&lt; Raindrops</a></li><li><a href="../reverse-string/README.zh.html"><strong aria-hidden="true">2.5.</strong> 反转字符串 &gt;&lt; Reverse String</a></li><li><a href="../nth-prime/README.zh.html"><strong aria-hidden="true">2.6.</strong> 第 n 个素数 &gt;&lt; Nth Prime</a></li><li><a href="../bob/README.zh.html"><strong aria-hidden="true">2.7.</strong> 迟钝孩子 &gt;&lt; Bob</a></li><li><a href="../beer-song/README.zh.html"><strong aria-hidden="true">2.8.</strong> 啤酒之歌 &gt;&lt; Beer Song</a></li><li><a href="../proverb/README.zh.html"><strong aria-hidden="true">2.9.</strong> 谚语串烧 &gt;&lt; Proverb</a></li><li><a href="../difference-of-squares/README.zh.html"><strong aria-hidden="true">2.10.</strong> 平方差 &gt;&lt; Difference Of Squares</a></li><li><a href="../sum-of-multiples/README.zh.html"><strong aria-hidden="true">2.11.</strong> 倍数之和 &gt;&lt; Sum Of Multiples</a></li><li><a href="../grains/README.zh.html"><strong aria-hidden="true">2.12.</strong> 谷物 &gt;&lt; Grains</a></li><li><a href="../pythagorean-triplet/README.zh.html"><strong aria-hidden="true">2.13.</strong> 勾股数 &gt;&lt; Pythagorean Triplet</a></li><li><a href="../prime-factors/README.zh.html"><strong aria-hidden="true">2.14.</strong> 素数因子 &gt;&lt; Prime Factors</a></li><li><a href="../series/README.zh.html"><strong aria-hidden="true">2.15.</strong> 子串 &gt;&lt; Series</a></li><li><a href="../armstrong-numbers/README.zh.html"><strong aria-hidden="true">2.16.</strong> 水仙花数 &gt;&lt; Armstrong Numbers</a></li><li><a href="../collatz-conjecture/README.zh.html"><strong aria-hidden="true">2.17.</strong> 3n+1 猜想 &gt;&lt; Collatz Conjecture</a></li><li><a href="../diffie-hellman/README.zh.html"><strong aria-hidden="true">2.18.</strong> 迪菲-赫尔曼密钥交换 &gt;&lt; Diffie Hellman</a></li></ol></li><li><a href="../medium.html"><strong aria-hidden="true">3.</strong> 中等</a></li><li><ol class="section"><li><a href="../saddle-points/README.zh.html"><strong aria-hidden="true">3.1.</strong> 鞍点 &gt;&lt; Saddle Points</a></li><li><a href="../isogram/README.zh.html"><strong aria-hidden="true">3.2.</strong> 等值线 &gt;&lt; Isogram</a></li><li><a href="../say/README.zh.html"><strong aria-hidden="true">3.3.</strong> 英文说数字 &gt;&lt; Say</a></li><li><a href="../run-length-encoding/README.zh.html"><strong aria-hidden="true">3.4.</strong> 游程编码 &gt;&lt; Run Length Encoding</a></li><li><a href="../isbn-verifier/README.zh.html"><strong aria-hidden="true">3.5.</strong> 图书编号 &gt;&lt; ISBN Verifier</a></li><li><a href="../perfect-numbers/README.zh.html"><strong aria-hidden="true">3.6.</strong> 数字也能分类 &gt;&lt; Perfect Numbers</a></li><li><a href="../clock/README.zh.html"><strong aria-hidden="true">3.7.</strong> 时钟 &gt;&lt; Clock</a></li><li><a href="../dot-dsl/README.zh.html"><strong aria-hidden="true">3.8.</strong> DOT DSL</a></li><li><a href="../hamming/README.zh.html"><strong aria-hidden="true">3.9.</strong> 汉明距离 &gt;&lt; Hamming</a></li><li><a href="../simple-linked-list/README.zh.html"><strong aria-hidden="true">3.10.</strong> 简单链表 &gt;&lt; Simple Linked List</a></li><li><a href="../pascals-triangle/README.zh.html"><strong aria-hidden="true">3.11.</strong> 杨辉三角形 &gt;&lt; Pascal's Triangle</a></li><li><a href="../scrabble-score/README.zh.html"><strong aria-hidden="true">3.12.</strong> 字母的分数游戏 &gt;&lt; Scrabble Score</a></li><li><a href="../pangram/README.zh.html"><strong aria-hidden="true">3.13.</strong> 全字母句 &gt;&lt; Pangram</a></li><li><a href="../paasio/README.zh.html"><strong aria-hidden="true">3.14.</strong> PaaS-IO-报告 &gt;&lt; Paasio</a></li><li><a href="../nucleotide-count/README.zh.html"><strong aria-hidden="true">3.15.</strong> 核苷酸计数 &gt;&lt; Nucleotide Count</a></li><li><a href="../luhn/README.zh.html"><strong aria-hidden="true">3.16.</strong> 模 10 算法 &gt;&lt; Luhn</a></li><li><a href="../largest-series-product/README.zh.html"><strong aria-hidden="true">3.17.</strong> 最大数字子串乘积 &gt;&lt; Largest Series Product</a></li><li><a href="../word-count/README.zh.html"><strong aria-hidden="true">3.18.</strong> 单词计数 &gt;&lt; Word Count</a></li><li><a href="../atbash-cipher/README.zh.html"><strong aria-hidden="true">3.19.</strong> Atbash 加密 &gt;&lt; Atbash Cipher</a></li><li><a href="../crypto-square/README.zh.html"><strong aria-hidden="true">3.20.</strong> 密码矩形 &gt;&lt; Crypto Square</a></li><li><a href="../rotational-cipher/README.zh.html"><strong aria-hidden="true">3.21.</strong> 旋转密码 &gt;&lt; Rotational Cipher</a></li><li><a href="../simple-cipher/README.zh.html"><strong aria-hidden="true">3.22.</strong> 简单加密 &gt;&lt; Simple Cipher</a></li><li><a href="../rail-fence-cipher/README.zh.html"><strong aria-hidden="true">3.23.</strong> 栅栏密码 &gt;&lt; Rail Fence Cipher</a></li><li><a href="../etl/README.zh.html"><strong aria-hidden="true">3.24.</strong> ETL</a></li><li><a href="../accumulate/README.zh.html"><strong aria-hidden="true">3.25.</strong> 集合操作 &gt;&lt; Accumulate</a></li><li><a href="../acronym/README.zh.html"><strong aria-hidden="true">3.26.</strong> 术语 &gt;&lt; Acronym</a></li><li><a href="../sieve/README.zh.html"><strong aria-hidden="true">3.27.</strong> 素数筛 &gt;&lt; Sieve</a></li><li><a href="../rna-transcription/README.zh.html"><strong aria-hidden="true">3.28.</strong> RNA 转录 &gt;&lt; RNA Transcription</a></li><li><a href="../triangle/README.zh.html"><strong aria-hidden="true">3.29.</strong> 三角形</a></li><li><a href="../roman-numerals/README.zh.html"><strong aria-hidden="true">3.30.</strong> 罗马数字 &gt;&lt; Roman Numerals</a></li><li><a href="../all-your-base/README.zh.html"><strong aria-hidden="true">3.31.</strong> 你所的基本</a></li><li><a href="../grade-school/README.zh.html"><strong aria-hidden="true">3.32.</strong> 学册</a></li><li><a href="../binary-search/README.zh.html"><strong aria-hidden="true">3.33.</strong> 二分查找</a></li><li><a href="../robot-simulator/README.zh.html"><strong aria-hidden="true">3.34.</strong> 机器人模拟器</a></li><li><a href="../bracket-push/README.zh.html"><strong aria-hidden="true">3.35.</strong> 括号配套</a></li><li><a href="../luhn-from/README.zh.html"><strong aria-hidden="true">3.36.</strong> Luhn From</a></li><li><a href="../queen-attack/README.zh.html"><strong aria-hidden="true">3.37.</strong> 皇后 攻击</a></li><li><a href="../bowling/README.zh.html"><strong aria-hidden="true">3.38.</strong> 保龄球</a></li><li><a href="../sublist/README.zh.html"><strong aria-hidden="true">3.39.</strong> 子列表</a></li><li><a href="../space-age/README.zh.html"><strong aria-hidden="true">3.40.</strong> 地球年</a></li><li><a href="../luhn-trait/README.zh.html"><strong aria-hidden="true">3.41.</strong> Luhn Trait</a></li><li><a href="../macros/README.zh.html"><strong aria-hidden="true">3.42.</strong> 宏</a></li><li><a href="../allergies/README.zh.html"><strong aria-hidden="true">3.43.</strong> 过敏</a></li><li><a href="../variable-length-quantity/README.zh.html"><strong aria-hidden="true">3.44.</strong> 可变长度数量</a></li><li><a href="../phone-number/README.zh.html"><strong aria-hidden="true">3.45.</strong> 电话号码</a></li><li><a href="../wordy/README.zh.html"><strong aria-hidden="true">3.46.</strong> 罗唆</a></li><li><a href="../tournament/README.zh.html"><strong aria-hidden="true">3.47.</strong> 比赛</a></li><li><a href="../custom-set/README.zh.html"><strong aria-hidden="true">3.48.</strong> 自定义 set</a></li><li><a href="../alphametics/README.zh.html"><strong aria-hidden="true">3.49.</strong> 字母谜题</a></li><li><a href="../two-bucket/README.zh.html"><strong aria-hidden="true">3.50.</strong> 两个桶</a></li><li><a href="../pig-latin/README.zh.html"><strong aria-hidden="true">3.51.</strong> 猪的拉丁文</a></li><li><a href="../diamond/README.zh.html"><strong aria-hidden="true">3.52.</strong> 钻石</a></li><li><a href="../spiral-matrix/README.zh.html"><strong aria-hidden="true">3.53.</strong> 螺旋矩阵</a></li><li><a href="../palindrome-products/README.zh.html"><strong aria-hidden="true">3.54.</strong> 回文产品</a></li><li><a href="../poker/README.zh.html"><strong aria-hidden="true">3.55.</strong> 扑克</a></li><li><a href="../grep/README.zh.html"><strong aria-hidden="true">3.56.</strong> grep</a></li><li><a href="../scale-generator/README.zh.html"><strong aria-hidden="true">3.57.</strong> 音阶生成器</a></li><li><a href="../decimal/README.zh.html"><strong aria-hidden="true">3.58.</strong> 十进制</a></li><li><a href="../anagram/README.zh.html"><strong aria-hidden="true">3.59.</strong> 字谜</a></li><li><a href="../protein-translation/README.zh.html"><strong aria-hidden="true">3.60.</strong> 蛋白质翻译</a></li><li><a href="../robot-name/README.zh.html"><strong aria-hidden="true">3.61.</strong> 机器人名称</a></li><li><a href="../book-store/README.zh.html"><strong aria-hidden="true">3.62.</strong> 书店</a></li></ol></li><li><a href="../high.html"><strong aria-hidden="true">4.</strong> 难</a></li><li><ol class="section"><li><a href="../ocr-numbers/README.zh.html"><strong aria-hidden="true">4.1.</strong> OCR 号码</a></li><li><a href="../minesweeper/README.zh.html"><strong aria-hidden="true">4.2.</strong> 扫雷</a></li><li><a href="../dominoes/README.zh.html"><strong aria-hidden="true">4.3.</strong> 骨牌</a></li><li><a href="../parallel-letter-frequency/README.zh.html"><strong aria-hidden="true">4.4.</strong> 并行字母频率</a></li><li><a href="../rectangles/README.zh.html"><strong aria-hidden="true">4.5.</strong> 矩形</a></li><li><a href="../forth/README.zh.html"><strong aria-hidden="true">4.6.</strong> Forth</a></li><li><a href="../circular-buffer/README.zh.html"><strong aria-hidden="true">4.7.</strong> 循环缓冲区</a></li><li><a href="../react/README.zh.html" class="active"><strong aria-hidden="true">4.8.</strong> React</a></li></ol></li><li><a href="../untag.html"><strong aria-hidden="true">5.</strong> 未标签</a></li><li><ol class="section"><li><a href="../hexadecimal/README.zh.html"><strong aria-hidden="true">5.1.</strong> 十六进制</a></li><li><a href="../nucleotide-codons/README.zh.html"><strong aria-hidden="true">5.2.</strong> 核苷酸密码子</a></li><li><a href="../two-fer/README.zh.html"><strong aria-hidden="true">5.3.</strong> two-fer</a></li><li class="spacer"></li></ol></li><li><a href="../add-test-code.html">修改 mdBook 主题</a></li></ol>
    </nav>

    <div id="page-wrapper" class="page-wrapper">

        <div class="page">
            
            <div id="menu-bar" class="menu-bar">
                <div id="menu-bar-sticky-container">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents"
                            aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <!-- START - Rust Cookbook customization -->
                        <button id="edit-button" class="icon-button" type="button" title="Fork and edit" aria-label="Fork and edit"
                            aria-haspopup="true" aria-expanded="false" aria-controls="edit">
                            <i class="fa fa-edit">Edit</i>
                        </button>
                        <!-- END - Rust Cookbook customization -->
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme"
                            aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)"
                            aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                        
                    </div>

                    <h1 class="menu-title">exercisms.io 快速练习</h1>
                    
                        <div class="right-buttons">
                            <a href="../print.html" title="Print this book" aria-label="Print this book">
                                <i id="print-button" class="fa fa-print"></i>
                            </a>
                            
                        </div>
                    </div>
                </div>

            
            <div id="search-wrapper" class="hidden">
                <form id="searchbar-outer" class="searchbar-outer">
                    <input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..."
                        aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                </form>
                <div id="searchresults-outer" class="searchresults-outer hidden">
                    <div id="searchresults-header" class="searchresults-header"></div>
                    <ul id="searchresults">
                    </ul>
                </div>
            </div>
            

            <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
            <script type="text/javascript">
                document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                Array.from(document.querySelectorAll('#sidebar a')).forEach(function (link) {
                    link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                });
            </script>

            <!-- // START - Rust Cookbook customization -->
            <script>
                document.getElementById("edit-button").addEventListener("click", function () {
                    var editWindow = window.open("https://github.com/chinanf-boy/exercism-rust-zh/edit/master/src/react/README.zh.md");
                });</script>
            <!-- // END - Rust Cookbook customization -->

            <div id="content" class="content">
                <main>
                    <a class="header" href="#react" id="react"><h1>React</h1></a>
<a class="header" href="#1-readme" id="1-readme"><h2>1. Readme</h2></a>
<a class="header" href="#react-1" id="react-1"><h1>React</h1></a>
<p>实现基本的 React 系统。</p>
<p>React 式编程是一种编程范例，它着重于如何根据彼此来计算值，以允许对一个值的更改，自动传播到其他值，如在电子表格中。</p>
<p>实现一个基本的 React 系统，其中，有带可设置值的单元(”输入”单元)，还有能根据其他单元计算值的单元(”计算”单元)。实现更新，以便当输入值改变时，（关联的）值会传播，直到一个新的稳定的系统状态。</p>
<p>此外，计算单元应该允许，注册更改通知的回调。当单元值，从之前的稳定状态到一个新的稳定状态时，调用一个单元格的回调。</p>
<a class="header" href="#2-开始你的表演" id="2-开始你的表演"><h2>2. 开始你的表演</h2></a>
<pre><pre class="playpen"><code class="language-rust editable">/// `InputCellID` is a unique identifier for an input cell.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct InputCellID();
/// `ComputeCellID` is a unique identifier for a compute cell.
/// Values of type `InputCellID` and `ComputeCellID` should not be mutually assignable,
/// demonstrated by the following tests:
///
/// ```compile_fail
/// let mut r = react::Reactor::new();
/// let input: react::ComputeCellID = r.create_input(111);
/// ```
///
/// ```compile_fail
/// let mut r = react::Reactor::new();
/// let input = r.create_input(111);
/// let compute: react::InputCellID = r.create_compute(&amp;[react::CellID::Input(input)], |_| 222).unwrap();
/// ```
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ComputeCellID();
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CallbackID();

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CellID {
   Input(InputCellID),
   Compute(ComputeCellID),
}

#[derive(Debug, PartialEq)]
pub enum RemoveCallbackError {
   NonexistentCell,
   NonexistentCallback,
}

pub struct Reactor&lt;T&gt; {
   // Just so that the compiler doesn't complain about an unused type parameter.
   // You probably want to delete this field.
   dummy: ::std::marker::PhantomData&lt;T&gt;,
}

// You are guaranteed that Reactor will only be tested against types that are Copy + PartialEq.
impl&lt;T: Copy + PartialEq&gt; Reactor&lt;T&gt; {
   pub fn new() -&gt; Self {
       unimplemented!()
   }

   // Creates an input cell with the specified initial value, returning its ID.
   pub fn create_input(&amp;mut self, _initial: T) -&gt; InputCellID {
       unimplemented!()
   }

   // Creates a compute cell with the specified dependencies and compute function.
   // The compute function is expected to take in its arguments in the same order as specified in
   // `dependencies`.
   // You do not need to reject compute functions that expect more arguments than there are
   // dependencies (how would you check for this, anyway?).
   //
   // If any dependency doesn't exist, returns an Err with that nonexistent dependency.
   // (If multiple dependencies do not exist, exactly which one is returned is not defined and
   // will not be tested)
   //
   // Notice that there is no way to *remove* a cell.
   // This means that you may assume, without checking, that if the dependencies exist at creation
   // time they will continue to exist as long as the Reactor exists.
   pub fn create_compute&lt;F: Fn(&amp;[T]) -&gt; T&gt;(
       &amp;mut self,
       _dependencies: &amp;[CellID],
       _compute_func: F,
   ) -&gt; Result&lt;ComputeCellID, CellID&gt; {
       unimplemented!()
   }

   // Retrieves the current value of the cell, or None if the cell does not exist.
   //
   // You may wonder whether it is possible to implement `get(&amp;self, id: CellID) -&gt; Option&lt;&amp;Cell&gt;`
   // and have a `value(&amp;self)` method on `Cell`.
   //
   // It turns out this introduces a significant amount of extra complexity to this exercise.
   // We chose not to cover this here, since this exercise is probably enough work as-is.
   pub fn value(&amp;self, id: CellID) -&gt; Option&lt;T&gt; {
       unimplemented!(&quot;Get the value of the cell whose id is {:?}&quot;, id)
   }

   // Sets the value of the specified input cell.
   //
   // Returns false if the cell does not exist.
   //
   // Similarly, you may wonder about `get_mut(&amp;mut self, id: CellID) -&gt; Option&lt;&amp;mut Cell&gt;`, with
   // a `set_value(&amp;mut self, new_value: T)` method on `Cell`.
   //
   // As before, that turned out to add too much extra complexity.
   pub fn set_value(&amp;mut self, _id: InputCellID, _new_value: T) -&gt; bool {
       unimplemented!()
   }

   // Adds a callback to the specified compute cell.
   //
   // Returns the ID of the just-added callback, or None if the cell doesn't exist.
   //
   // Callbacks on input cells will not be tested.
   //
   // The semantics of callbacks (as will be tested):
   // For a single set_value call, each compute cell's callbacks should each be called:
   // * Zero times if the compute cell's value did not change as a result of the set_value call.
   // * Exactly once if the compute cell's value changed as a result of the set_value call.
   //   The value passed to the callback should be the final value of the compute cell after the
   //   set_value call.
   pub fn add_callback&lt;F: FnMut(T) -&gt; ()&gt;(
       &amp;mut self,
       _id: ComputeCellID,
       _callback: F,
   ) -&gt; Option&lt;CallbackID&gt; {
       unimplemented!()
   }

   // Removes the specified callback, using an ID returned from add_callback.
   //
   // Returns an Err if either the cell or callback does not exist.
   //
   // A removed callback should no longer be called.
   pub fn remove_callback(
       &amp;mut self,
       cell: ComputeCellID,
       callback: CallbackID,
   ) -&gt; Result&lt;(), RemoveCallbackError&gt; {
       unimplemented!(
           &quot;Remove the callback identified by the CallbackID {:?} from the cell {:?}&quot;,
           callback,
           cell,
       )
   }
}

</code></pre></pre>
<a class="header" href="#3-测试代码查看" id="3-测试代码查看"><h2>3. 测试代码查看</h2></a>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[test]
fn input_cells_have_a_value() {
   let mut reactor = Reactor::new();
   let input = reactor.create_input(10);
   assert_eq!(reactor.value(CellID::Input(input)), Some(10));
}

#[test]
//#[ignore]
fn an_input_cells_value_can_be_set() {
   let mut reactor = Reactor::new();
   let input = reactor.create_input(4);
   assert!(reactor.set_value(input, 20));
   assert_eq!(reactor.value(CellID::Input(input)), Some(20));
}

#[test]
//#[ignore]
fn error_setting_a_nonexistent_input_cell() {
   let mut dummy_reactor = Reactor::new();
   let input = dummy_reactor.create_input(1);
   assert!(!Reactor::new().set_value(input, 0));
}

#[test]
//#[ignore]
fn compute_cells_calculate_initial_value() {
   let mut reactor = Reactor::new();
   let input = reactor.create_input(1);
   let output = reactor
       .create_compute(&amp;[CellID::Input(input)], |v| v[0] + 1)
       .unwrap();
   assert_eq!(reactor.value(CellID::Compute(output)), Some(2));
}

#[test]
//#[ignore]
fn compute_cells_take_inputs_in_the_right_order() {
   let mut reactor = Reactor::new();
   let one = reactor.create_input(1);
   let two = reactor.create_input(2);
   let output = reactor
       .create_compute(&amp;[CellID::Input(one), CellID::Input(two)], |v| {
           v[0] + v[1] * 10
       })
       .unwrap();
   assert_eq!(reactor.value(CellID::Compute(output)), Some(21));
}

#[test]
//#[ignore]
fn error_creating_compute_cell_if_input_doesnt_exist() {
   let mut dummy_reactor = Reactor::new();
   let input = dummy_reactor.create_input(1);
   assert_eq!(
       Reactor::new().create_compute(&amp;[CellID::Input(input)], |_| 0),
       Err(CellID::Input(input))
   );
}

#[test]
//#[ignore]
fn do_not_break_cell_if_creating_compute_cell_with_valid_and_invalid_input() {
   let mut dummy_reactor = Reactor::new();
   let _ = dummy_reactor.create_input(1);
   let dummy_cell = dummy_reactor.create_input(2);
   let mut reactor = Reactor::new();
   let input = reactor.create_input(1);
   assert_eq!(
       reactor.create_compute(&amp;[CellID::Input(input), CellID::Input(dummy_cell)], |_| 0),
       Err(CellID::Input(dummy_cell))
   );
   assert!(reactor.set_value(input, 5));
   assert_eq!(reactor.value(CellID::Input(input)), Some(5));
}

#[test]
//#[ignore]
fn compute_cells_update_value_when_dependencies_are_changed() {
   let mut reactor = Reactor::new();
   let input = reactor.create_input(1);
   let output = reactor
       .create_compute(&amp;[CellID::Input(input)], |v| v[0] + 1)
       .unwrap();
   assert_eq!(reactor.value(CellID::Compute(output)), Some(2));
   assert!(reactor.set_value(input, 3));
   assert_eq!(reactor.value(CellID::Compute(output)), Some(4));
}

#[test]
//#[ignore]
fn compute_cells_can_depend_on_other_compute_cells() {
   let mut reactor = Reactor::new();
   let input = reactor.create_input(1);
   let times_two = reactor
       .create_compute(&amp;[CellID::Input(input)], |v| v[0] * 2)
       .unwrap();
   let times_thirty = reactor
       .create_compute(&amp;[CellID::Input(input)], |v| v[0] * 30)
       .unwrap();
   let output = reactor
       .create_compute(
           &amp;[CellID::Compute(times_two), CellID::Compute(times_thirty)],
           |v| v[0] + v[1],
       )
       .unwrap();
   assert_eq!(reactor.value(CellID::Compute(output)), Some(32));
   assert!(reactor.set_value(input, 3));
   assert_eq!(reactor.value(CellID::Compute(output)), Some(96));
}

/// A CallbackRecorder helps tests whether callbacks get called correctly.
/// You'll see it used in tests that deal with callbacks.
/// The names should be descriptive enough so that the tests make sense,
/// so it's not necessary to fully understand the implementation,
/// though you are welcome to.
struct CallbackRecorder {
   // Note that this `Cell` is https://doc.rust-lang.org/std/cell/
   // a mechanism to allow internal mutability,
   // distinct from the cells (input cells, compute cells) in the reactor
   value: std::cell::Cell&lt;Option&lt;i32&gt;&gt;,
}

impl CallbackRecorder {
   fn new() -&gt; Self {
       CallbackRecorder {
           value: std::cell::Cell::new(None),
       }
   }

   fn expect_to_have_been_called_with(&amp;self, v: i32) {
       assert_ne!(
           self.value.get(),
           None,
           &quot;Callback was not called, but should have been&quot;
       );
       assert_eq!(
           self.value.replace(None),
           Some(v),
           &quot;Callback was called with incorrect value&quot;
       );
   }

   fn expect_not_to_have_been_called(&amp;self) {
       assert_eq!(
           self.value.get(),
           None,
           &quot;Callback was called, but should not have been&quot;
       );
   }

   fn callback_called(&amp;self, v: i32) {
       assert_eq!(
           self.value.replace(Some(v)),
           None,
           &quot;Callback was called too many times; can't be called with {}&quot;,
           v
       );
   }
}

#[test]
//#[ignore]
fn compute_cells_fire_callbacks() {
   let cb = CallbackRecorder::new();
   let mut reactor = Reactor::new();
   let input = reactor.create_input(1);
   let output = reactor
       .create_compute(&amp;[CellID::Input(input)], |v| v[0] + 1)
       .unwrap();
   assert!(reactor
       .add_callback(output, |v| cb.callback_called(v))
       .is_some());
   assert!(reactor.set_value(input, 3));
   cb.expect_to_have_been_called_with(4);
}

#[test]
//#[ignore]
fn error_adding_callback_to_nonexistent_cell() {
   let mut dummy_reactor = Reactor::new();
   let input = dummy_reactor.create_input(1);
   let output = dummy_reactor
       .create_compute(&amp;[CellID::Input(input)], |_| 0)
       .unwrap();
   assert_eq!(
       Reactor::new().add_callback(output, |_: u32| println!(&quot;hi&quot;)),
       None
   );
}

#[test]
//#[ignore]
fn callbacks_only_fire_on_change() {
   let cb = CallbackRecorder::new();
   let mut reactor = Reactor::new();
   let input = reactor.create_input(1);
   let output = reactor
       .create_compute(
           &amp;[CellID::Input(input)],
           |v| if v[0] &lt; 3 { 111 } else { 222 },
       )
       .unwrap();
   assert!(reactor
       .add_callback(output, |v| cb.callback_called(v))
       .is_some());

   assert!(reactor.set_value(input, 2));
   cb.expect_not_to_have_been_called();
   assert!(reactor.set_value(input, 4));
   cb.expect_to_have_been_called_with(222);
}

#[test]
//#[ignore]
fn callbacks_can_be_added_and_removed() {
   let cb1 = CallbackRecorder::new();
   let cb2 = CallbackRecorder::new();
   let cb3 = CallbackRecorder::new();

   let mut reactor = Reactor::new();
   let input = reactor.create_input(11);
   let output = reactor
       .create_compute(&amp;[CellID::Input(input)], |v| v[0] + 1)
       .unwrap();

   let callback = reactor
       .add_callback(output, |v| cb1.callback_called(v))
       .unwrap();
   assert!(reactor
       .add_callback(output, |v| cb2.callback_called(v))
       .is_some());

   assert!(reactor.set_value(input, 31));
   cb1.expect_to_have_been_called_with(32);
   cb2.expect_to_have_been_called_with(32);

   assert!(reactor.remove_callback(output, callback).is_ok());
   assert!(reactor
       .add_callback(output, |v| cb3.callback_called(v))
       .is_some());

   assert!(reactor.set_value(input, 41));
   cb1.expect_not_to_have_been_called();
   cb2.expect_to_have_been_called_with(42);
   cb3.expect_to_have_been_called_with(42);
}

#[test]
//#[ignore]
fn removing_a_callback_multiple_times_doesnt_interfere_with_other_callbacks() {
   let cb1 = CallbackRecorder::new();
   let cb2 = CallbackRecorder::new();

   let mut reactor = Reactor::new();
   let input = reactor.create_input(1);
   let output = reactor
       .create_compute(&amp;[CellID::Input(input)], |v| v[0] + 1)
       .unwrap();
   let callback = reactor
       .add_callback(output, |v| cb1.callback_called(v))
       .unwrap();
   assert!(reactor
       .add_callback(output, |v| cb2.callback_called(v))
       .is_some());
   // We want the first remove to be Ok, but the others should be errors.
   assert!(reactor.remove_callback(output, callback).is_ok());
   for _ in 1..5 {
       assert_eq!(
           reactor.remove_callback(output, callback),
           Err(RemoveCallbackError::NonexistentCallback)
       );
   }

   assert!(reactor.set_value(input, 2));
   cb1.expect_not_to_have_been_called();
   cb2.expect_to_have_been_called_with(3);
}

#[test]
//#[ignore]
fn callbacks_should_only_be_called_once_even_if_multiple_dependencies_change() {
   let cb = CallbackRecorder::new();
   let mut reactor = Reactor::new();
   let input = reactor.create_input(1);
   let plus_one = reactor
       .create_compute(&amp;[CellID::Input(input)], |v| v[0] + 1)
       .unwrap();
   let minus_one1 = reactor
       .create_compute(&amp;[CellID::Input(input)], |v| v[0] - 1)
       .unwrap();
   let minus_one2 = reactor
       .create_compute(&amp;[CellID::Compute(minus_one1)], |v| v[0] - 1)
       .unwrap();
   let output = reactor
       .create_compute(
           &amp;[CellID::Compute(plus_one), CellID::Compute(minus_one2)],
           |v| v[0] * v[1],
       )
       .unwrap();
   assert!(reactor
       .add_callback(output, |v| cb.callback_called(v))
       .is_some());
   assert!(reactor.set_value(input, 4));
   cb.expect_to_have_been_called_with(10);
}

#[test]
//#[ignore]
fn callbacks_should_not_be_called_if_dependencies_change_but_output_value_doesnt_change() {
   let cb = CallbackRecorder::new();
   let mut reactor = Reactor::new();
   let input = reactor.create_input(1);
   let plus_one = reactor
       .create_compute(&amp;[CellID::Input(input)], |v| v[0] + 1)
       .unwrap();
   let minus_one = reactor
       .create_compute(&amp;[CellID::Input(input)], |v| v[0] - 1)
       .unwrap();
   let always_two = reactor
       .create_compute(
           &amp;[CellID::Compute(plus_one), CellID::Compute(minus_one)],
           |v| v[0] - v[1],
       )
       .unwrap();
   assert!(reactor
       .add_callback(always_two, |v| cb.callback_called(v))
       .is_some());
   for i in 2..5 {
       assert!(reactor.set_value(input, i));
       cb.expect_not_to_have_been_called();
   }
}

#[test]
//#[ignore]
fn test_adder_with_boolean_values() {
   // This is a digital logic circuit called an adder:
   // https://en.wikipedia.org/wiki/Adder_(electronics)
   let mut reactor = Reactor::new();
   let a = reactor.create_input(false);
   let b = reactor.create_input(false);
   let carry_in = reactor.create_input(false);

   let a_xor_b = reactor
       .create_compute(&amp;[CellID::Input(a), CellID::Input(b)], |v| v[0] ^ v[1])
       .unwrap();
   let sum = reactor
       .create_compute(&amp;[CellID::Compute(a_xor_b), CellID::Input(carry_in)], |v| {
           v[0] ^ v[1]
       })
       .unwrap();

   let a_xor_b_and_cin = reactor
       .create_compute(&amp;[CellID::Compute(a_xor_b), CellID::Input(carry_in)], |v| {
           v[0] &amp;&amp; v[1]
       })
       .unwrap();
   let a_and_b = reactor
       .create_compute(&amp;[CellID::Input(a), CellID::Input(b)], |v| v[0] &amp;&amp; v[1])
       .unwrap();
   let carry_out = reactor
       .create_compute(
           &amp;[CellID::Compute(a_xor_b_and_cin), CellID::Compute(a_and_b)],
           |v| v[0] || v[1],
       )
       .unwrap();

   let tests = &amp;[
       (false, false, false, false, false),
       (false, false, true, false, true),
       (false, true, false, false, true),
       (false, true, true, true, false),
       (true, false, false, false, true),
       (true, false, true, true, false),
       (true, true, false, true, false),
       (true, true, true, true, true),
   ];

   for &amp;(aval, bval, cinval, expected_cout, expected_sum) in tests {
       assert!(reactor.set_value(a, aval));
       assert!(reactor.set_value(b, bval));
       assert!(reactor.set_value(carry_in, cinval));

       assert_eq!(reactor.value(CellID::Compute(sum)), Some(expected_sum));
       assert_eq!(
           reactor.value(CellID::Compute(carry_out)),
           Some(expected_cout)
       );
   }
}

#}</code></pre></pre>
<a class="header" href="#4-答案" id="4-答案"><h2>4. 答案</h2></a>
<p><details></p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
use std::collections::HashMap;

/// `InputCellID` is a unique identifier for an input cell.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct InputCellID(usize);
/// `ComputeCellID` is a unique identifier for a compute cell.
/// Values of type `InputCellID` and `ComputeCellID` should not be mutually assignable,
/// demonstrated by the following tests:
///
/// ```compile_fail
/// let mut r = react::Reactor::new();
/// let input: react::ComputeCellID = r.create_input(111);
/// ```
///
/// ```compile_fail
/// let mut r = react::Reactor::new();
/// let input = r.create_input(111);
/// let compute: react::InputCellID = r.create_compute(&amp;[react::CellID::Input(input)], |_| 222).unwrap();
/// ```
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ComputeCellID(usize);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct CallbackID(usize);

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CellID {
   Input(InputCellID),
   Compute(ComputeCellID),
}

#[derive(Debug, PartialEq)]
pub enum RemoveCallbackError {
   NonexistentCell,
   NonexistentCallback,
}

struct Cell&lt;T: Copy&gt; {
   value: T,
   last_value: T,
   dependents: Vec&lt;ComputeCellID&gt;,
}

struct ComputeCell&lt;'a, T: Copy&gt; {
   cell: Cell&lt;T&gt;,

   dependencies: Vec&lt;CellID&gt;,
   f: Box&lt;Fn(&amp;[T]) -&gt; T + 'a&gt;,
   callbacks_issued: usize,
   callbacks: HashMap&lt;CallbackID, Box&lt;FnMut(T) -&gt; () + 'a&gt;&gt;,
}

impl&lt;T: Copy&gt; Cell&lt;T&gt; {
   fn new(initial: T) -&gt; Self {
       Cell {
           value: initial,
           last_value: initial,
           dependents: Vec::new(),
       }
   }
}

impl&lt;'a, T: Copy&gt; ComputeCell&lt;'a, T&gt; {
   fn new&lt;F: Fn(&amp;[T]) -&gt; T + 'a&gt;(initial: T, dependencies: Vec&lt;CellID&gt;, f: F) -&gt; Self {
       ComputeCell {
           cell: Cell::new(initial),

           dependencies,
           f: Box::new(f),
           callbacks_issued: 0,
           callbacks: HashMap::new(),
       }
   }
}

pub struct Reactor&lt;'a, T: Copy&gt; {
   inputs: Vec&lt;Cell&lt;T&gt;&gt;,
   computes: Vec&lt;ComputeCell&lt;'a, T&gt;&gt;,
}

impl&lt;'a, T: Copy + PartialEq&gt; Reactor&lt;'a, T&gt; {
   pub fn new() -&gt; Self {
       Reactor {
           inputs: Vec::new(),
           computes: Vec::new(),
       }
   }

   pub fn create_input(&amp;mut self, initial: T) -&gt; InputCellID {
       self.inputs.push(Cell::new(initial));
       InputCellID(self.inputs.len() - 1)
   }

   pub fn create_compute&lt;F: Fn(&amp;[T]) -&gt; T + 'a&gt;(
       &amp;mut self,
       dependencies: &amp;[CellID],
       compute_func: F,
   ) -&gt; Result&lt;ComputeCellID, CellID&gt; {
       // Check all dependencies' validity before modifying any of them,
       // so that we don't perform an incorrect partial write.
       for &amp;dep in dependencies {
           match dep {
               CellID::Input(InputCellID(id)) =&gt; if id &gt;= self.inputs.len() {
                   return Err(dep);
               },
               CellID::Compute(ComputeCellID(id)) =&gt; if id &gt;= self.computes.len() {
                   return Err(dep);
               },
           }
       }
       let new_id = ComputeCellID(self.computes.len());
       for &amp;dep in dependencies {
           match dep {
               CellID::Input(InputCellID(id)) =&gt; self.inputs[id].dependents.push(new_id),
               CellID::Compute(ComputeCellID(id)) =&gt; {
                   self.computes[id].cell.dependents.push(new_id)
               }
           }
       }
       let inputs: Vec&lt;_&gt; = dependencies
           .iter()
           .map(|&amp;id| self.value(id).unwrap())
           .collect();
       let initial = compute_func(&amp;inputs);
       self.computes.push(ComputeCell::new(
           initial,
           dependencies.to_vec(),
           compute_func,
       ));
       Ok(new_id)
   }

   pub fn value(&amp;self, id: CellID) -&gt; Option&lt;T&gt; {
       match id {
           CellID::Input(InputCellID(id)) =&gt; self.inputs.get(id).map(|c| c.value),
           CellID::Compute(ComputeCellID(id)) =&gt; self.computes.get(id).map(|c| c.cell.value),
       }
   }

   pub fn set_value(&amp;mut self, id: InputCellID, new_value: T) -&gt; bool {
       let InputCellID(id) = id;
       self.inputs
           .get_mut(id)
           .map(|c| {
               c.value = new_value;
               c.dependents.clone()
           })
           .map(|deps| {
               for &amp;d in deps.iter() {
                   self.update_dependent(d);
               }
               // We can only fire callbacks after all dependents have updated.
               // So we can't combine this for loop with the one above!
               for d in deps {
                   self.fire_callbacks(d);
               }
           })
           .is_some()
   }

   pub fn add_callback&lt;F: FnMut(T) -&gt; () + 'a&gt;(
       &amp;mut self,
       id: ComputeCellID,
       callback: F,
   ) -&gt; Option&lt;CallbackID&gt; {
       let ComputeCellID(id) = id;
       self.computes.get_mut(id).map(|c| {
           c.callbacks_issued += 1;
           let cbid = CallbackID(c.callbacks_issued);
           c.callbacks.insert(cbid, Box::new(callback));
           cbid
       })
   }

   pub fn remove_callback(
       &amp;mut self,
       cell: ComputeCellID,
       callback: CallbackID,
   ) -&gt; Result&lt;(), RemoveCallbackError&gt; {
       let ComputeCellID(cell) = cell;
       match self.computes.get_mut(cell) {
           Some(c) =&gt; match c.callbacks.remove(&amp;callback) {
               Some(_) =&gt; Ok(()),
               None =&gt; Err(RemoveCallbackError::NonexistentCallback),
           },
           None =&gt; Err(RemoveCallbackError::NonexistentCell),
       }
   }

   fn update_dependent(&amp;mut self, id: ComputeCellID) {
       let ComputeCellID(id) = id;

       let (new_value, dependents) = {
           // This block limits the scope of the self.cells borrow.
           // This is necessary because we borrow it mutably below.
           let (dependencies, f, dependents) = match self.computes.get(id) {
               Some(c) =&gt; (&amp;c.dependencies, &amp;c.f, c.cell.dependents.clone()),
               None =&gt; panic!(&quot;Cell to update disappeared while querying&quot;),
           };
           let inputs: Vec&lt;_&gt; = dependencies
               .iter()
               .map(|&amp;id| self.value(id).unwrap())
               .collect();
           (f(&amp;inputs), dependents)
       };

       match self.computes.get_mut(id) {
           Some(c) =&gt; {
               if c.cell.value == new_value {
                   // No change here, we don't need to update our dependents.
                   // (It wouldn't hurt to, but it would be unnecessary work)
                   return;
               }
               c.cell.value = new_value;
           }
           None =&gt; panic!(&quot;Cell to update disappeared while updating&quot;),
       }

       for d in dependents {
           self.update_dependent(d);
       }
   }

   fn fire_callbacks(&amp;mut self, id: ComputeCellID) {
       let ComputeCellID(id) = id;
       let dependents = match self.computes.get_mut(id) {
           Some(c) =&gt; {
               if c.cell.value == c.cell.last_value {
                   // Value hasn't changed since last callback fire.
                   // We thus shouldn't fire the callbacks.
                   return;
               }
               for cb in c.callbacks.values_mut() {
                   cb(c.cell.value);
               }
               c.cell.last_value = c.cell.value;
               c.cell.dependents.clone()
           }
           None =&gt; panic!(&quot;Callback cell disappeared&quot;),
       };

       for d in dependents {
           self.fire_callbacks(d);
       }
   }
}

#}</code></pre></pre>
<p></details></p>
<hr />
<hr />
<a class="header" href="#填充相关" id="填充相关"><h2>填充/相关</h2></a>

                </main>

                <nav class="nav-wrapper" aria-label="Page navigation">
                    <!-- Mobile navigation buttons -->
                    
                    <a rel="prev" href="../circular-buffer/README.zh.html" class="mobile-nav-chapters previous" title="Previous chapter"
                        aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>
                    

                    
                    <a rel="next" href="../untag.html" class="mobile-nav-chapters next" title="Next chapter"
                        aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
                    

                    <div style="clear: both"></div>
                </nav>
            </div>
        </div>

        <nav class="nav-wide-wrapper" aria-label="Page navigation">
            
            <a href="../circular-buffer/README.zh.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter"
                aria-keyshortcuts="Left">
                <i class="fa fa-angle-left"></i>
            </a>
            

            
            <a href="../untag.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter"
                aria-keyshortcuts="Right">
                <i class="fa fa-angle-right"></i>
            </a>
            
        </nav>

    </div>

    

    
    <!-- Google Analytics Tag -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-128555056-1"></script>
    
    <script type="text/javascript">
        var localAddrs = ["localhost", "127.0.0.1", ""];
        if (localAddrs.indexOf(document.location.hostname) === -1) {
            window.dataLayer = window.dataLayer || [];
            function gtag() { dataLayer.push(arguments); }
            gtag('js', new Date());

            gtag('config', 'UA-128555056-1');
        }
    </script>
    

    
    <script src="../ace.js" type="text/javascript" charset="utf-8"></script>
    <script src="../editor.js" type="text/javascript" charset="utf-8"></script>
    <script src="../mode-rust.js" type="text/javascript" charset="utf-8"></script>
    <script src="../theme-dawn.js" type="text/javascript" charset="utf-8"></script>
    <script src="../theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
    

    
    <script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
    <script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
    <script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
    

    <script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
    <script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
    <script src="../book.js" type="text/javascript" charset="utf-8"></script>

    <!-- Custom JS scripts -->
    

    

</body>

</html>